package com.i2863.shiroDemo.config;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.i2863.shiroDemo.dto.UserInfo;
import com.i2863.shiroDemo.ex.RoleAuthException;
import com.i2863.shiroDemo.ex.RoleDisableException;
import com.i2863.shiroDemo.entity.Permission;
import com.i2863.shiroDemo.entity.Role;
import com.i2863.shiroDemo.entity.User;
import com.i2863.shiroDemo.service.ILoginLogService;
import com.i2863.shiroDemo.service.IPermissionService;
import com.i2863.shiroDemo.service.IRoleService;
import com.i2863.shiroDemo.service.IUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.context.annotation.Lazy;

import javax.annotation.Resource;
import java.util.List;

@Slf4j
public class ShiroRealm extends AuthorizingRealm {

    @Resource
    @Lazy
    private IUserService iUserService;
    @Resource
    @Lazy
    private ILoginLogService iloginLogService;
    @Resource
    @Lazy
    private IPermissionService permissionService;
    @Resource
    @Lazy
    private IRoleService roleService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        UserInfo userInfo = (UserInfo) principals.getPrimaryPrincipal();
        userInfo = iUserService.findUserInfo(userInfo.getUserName());
        authorizationInfo.addRole(roleService.getById(userInfo.getRoleInfo().getId()).getRoleCode());
        userInfo.getRoleInfo().getPermissions().forEach(p -> authorizationInfo.addStringPermission(p.getPermissionCode()));
        //授权成功添加登录日志
        iloginLogService.addLoginLog(userInfo, SecurityUtils.getSubject().getSession());
        return authorizationInfo;
    }

    // 主要是用来进行身份认证的，也就是说验证用户输入的账号和密码是否正确
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        //获取用户的输入的账号.
        String username = (String) token.getPrincipal();
        //通过username从数据库中查找 User对象，如果找到，没找到.
        //实际项目中，这里可以根据实际情况做缓存，如果不做，Shiro自己也是有时间间隔机制，2分钟内不会重复执行该方法
        UserInfo userInfo = iUserService.findUserInfo(username);
        if (userInfo == null) {
            throw new AccountException();
        } else if (userInfo.getState() == 0) {
            throw new DisabledAccountException();
        } else if (userInfo.getState() == 2) {
            throw new LockedAccountException();
        } else if (userInfo.getRoleInfo().getPermissionIds() == null) {
            throw new RoleDisableException("该用户角色已被禁用！");
        }
        String perStr = roleService.getById(userInfo.getRoleInfo().getId()).getPermissionIds();
        if (StringUtils.isEmpty(perStr)) {
            throw new RoleAuthException("该用户角色无任何权限，不能登录！");
        }
        //保存登录用户ID
        Session session = SecurityUtils.getSubject().getSession();
        session.setAttribute(Constant.LOGIN_USER_ID, userInfo.getId());
        //保存超级管理员信息
        Permission permission = permissionService.getOne(new QueryWrapper<Permission>().eq("permission_code", "*"));
        session.setAttribute("superPermission", permission);

        Role role = roleService.getOne(new QueryWrapper<Role>().like("permission_ids", "," + permission.getId() + ","));
        session.setAttribute("superRole", role);
        List<User> users = iUserService.list(new QueryWrapper<User>().eq("role_id", role.getId()));
        session.setAttribute("superUsers", users);
        session.setAttribute("superAdmin", false);
        for (User user : users) {
            if (user.getId().intValue() == userInfo.getId().intValue()) {
                session.setAttribute("superAdmin", true);
            }
        }
        //  用户信息 密码 salt=username+salt realm name
        return new SimpleAuthenticationInfo(userInfo, userInfo.getPassWord(), ByteSource.Util.bytes(userInfo.getCredentialsSalt()), getName());
    }

    public void clearAuth() {
        this.clearCachedAuthorizationInfo(SecurityUtils.getSubject().getPrincipals());
    }
}